package com.common.utils.excel;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import com.common.annotation.ExcelColumn;
import com.common.utils.ConstantPropUtil;
import com.common.utils.StringUtils;

/**
 * POI生成excel
* @author 	zh
* @date 2018年2月2日
 */
public class POIExcel<T> {
	
	/**
	 * 自定义列头生成excel，生成类中除serialVersionUID外的所有字段，    .xlsx格式
	* @param headers	列头，需注意与是实体中字段的一一对应
	* @param dataset	数据集
	* @param out		输出流
	* @param pattern	日期格式，默认 yyyy-MM-dd HH:mm:ss
	* @throws Exception
	* @return void    
	* @Author:   zh
	* @Create Date:   2018年2月6日
	 */
	public void exportExcel(String[] headers,Collection<T> dataset,OutputStream out,
			String pattern)throws Exception {
		this.exportExcel(headers, dataset, out, pattern,null);
	}
	
	/**
	 * 自定义列头生成excel，生成类中除serialVersionUID外的所有字段，    .xlsx格式
	* @param headers	列头，需注意与是实体中字段的一一对应
	* @param dataset	数据集
	* @param out		输出流
	* @param pattern	日期格式，默认 yyyy-MM-dd HH:mm:ss
	* @param handler    生成数据前的数据处理回调函数
	* @throws Exception
	* @return void    
	* @Author:   zh
	* @Create Date:   2018年2月6日
	 */
	public void exportExcel(String[] headers,Collection<T> dataset,OutputStream out,
			String pattern,ExcelDataHandler<T> handler) throws Exception {
		if (headers == null || headers.length == 0) {
			throw new Exception("请输入列头信息");
		}
		if (StringUtils.isEmpty(pattern)) {
			pattern = "yyyy-MM-dd HH:mm:ss";
		}
		if (handler != null) {
			dataset = handler.handle(dataset);
		}
		DateFormat dft = new SimpleDateFormat(pattern);
		Workbook wb = new SXSSFWorkbook(100);  
	    Sheet sheet = null;     //工作表对象  
	    Row row = null;        //行对象  
	    Cell cell = null;      //列对象  
	    String textValue = null,fieldName = null;
	    int rowNum = 1,j=0;		
	    int bigestNum = Integer.valueOf(ConstantPropUtil.getProperty("excel_sheet_rows"));
	    Object value = null;
	    CellStyle cellStyle = this.createCellStyle(wb);
	    Iterator<T> it = dataset.iterator();  
	    Field[] fields = null;
	    sheet = wb.createSheet();
	    T vo = null;
	    this.createHead(headers, sheet, wb);
	    while (it.hasNext()){
	    	if (rowNum > bigestNum) {
	    		sheet = wb.createSheet();
	    		this.createHead(headers, sheet, wb);
	    		rowNum = 1;
			}
			row = sheet.createRow(rowNum);
			row.setHeightInPoints((short)15);
			rowNum++;
			j = 0;
			vo = it.next();
			if (handler != null) {
				vo = handler.singleHandle(vo);
			}
			fields = vo.getClass().getDeclaredFields();
			for(Field field : fields){
				fieldName = field.getName();  
				if (fieldName.equalsIgnoreCase("serialVersionUID")) {
					continue;
				}
				cell = row.createCell(j);
				cell.setCellStyle(cellStyle);
				j++;
				field.setAccessible(true);
				value = field.get(vo);
				if (value == null) {
					textValue = "";
				}else if (value instanceof Date) {
					textValue = dft.format(value);
				}else{
					textValue = value.toString();
				}
				cell.setCellValue(textValue);
			}
		};
	    
        wb.write(out);  
	}
	/**
	 * 根据注解ExcelColumn生成excel，  .xlsx格式
	* @param dataset  	数据集
	* @param out		输出流
	* @param vo			用于生成列头，不写进数据
	* @throws Exception
	* @return void    
	* @Author:   zh
	* @Create Date:   2018年2月6日
	 */
	public void exportExcel(Collection<T> dataset,OutputStream out,T vo) 
			throws Exception{
		this.exportExcel(dataset, out, vo,null);
	}

	/**
	 * 根据注解ExcelColumn生成excel，  .xlsx格式
	* @param dataset  	数据集
	* @param out		输出流
	* @param vo			用于生成列头，不写进数据
	* @param handler    生成数据前的数据处理回调函数
	* @throws Exception
	* @return void    
	* @Author:   zh
	* @Create Date:   2018年2月6日
	 */
	public void exportExcel(Collection<T> dataset,OutputStream out,T vo,
			ExcelDataHandler<T> handler) throws Exception{
		if (handler != null) {
			dataset = handler.handle(dataset);
		}
		DateFormat dft = null;
		List<EffectiveField> list = new ArrayList<>();
	    Workbook wb = new SXSSFWorkbook(100);  
		Row row = null;
		Cell cell = null;
		CellStyle cellStyle = this.createCellStyle(wb);
		Sheet sheet = wb.createSheet();
		Iterator<T> it = dataset.iterator();
		int rowNum = 1,j = 0;
		int bigestNum = Integer.valueOf(ConstantPropUtil.getProperty("excel_sheet_rows"));
		Object value = null;
		String txtValue = "";
		String oldFormat = "";
		Field field = null;
		list = this.analysisFields(vo);
		this.createHeader(list, sheet, wb);
		while (it.hasNext()) {
			//到达单个sheet的最大行数，则新建sheet
			if (rowNum > bigestNum) {
	    		sheet = wb.createSheet();
	    		this.createHeader(list, sheet, wb);
	    		rowNum = 1;
			}
			vo = it.next();
			if (handler != null) {
				vo = handler.singleHandle(vo);
			}
			row = sheet.createRow(rowNum);
			row.setHeightInPoints((short)15);
			rowNum++;
			j = 0;
			for(EffectiveField f : list){
				cell = row.createCell(j);
				j++;
				cell.setCellStyle(cellStyle);
//				cell.setCellType(SXSSFCell.CELL_TYPE_STRING);
				field = f.getField();
				field.setAccessible(true);
				value = field.get(vo);
				if (value == null) {
					txtValue = "";
				}else if (value instanceof Date) {
					if (dft != null && oldFormat.equals(f.getFormat())) {
						txtValue = dft.format(value);
					}else{
						dft = new SimpleDateFormat(f.getFormat());
						oldFormat = f.getFormat();
						txtValue = dft.format(value);
					}
				}else {
					txtValue = value.toString();
				}
				cell.setCellValue(txtValue);
			}
		};
		
		wb.write(out);
	}
	/**
	 * 创建数据单元格格式
	* @param wb
	* @return
	* @return CellStyle    
	* @Author:   zh
	* @Create Date:   2018年2月2日
	 */
	private CellStyle createCellStyle(Workbook wb){
		CellStyle style2 = wb.createCellStyle(); 
		style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);  
        style2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); 
        //全部设为文本格式
        DataFormat format = wb.createDataFormat();
        style2.setDataFormat(format.getFormat("@")); 
        // 生成另一个字体  
        Font font2 = wb.createFont();  
        font2.setColor(HSSFColor.BLACK.index);
        font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);  
        // 把字体应用到当前的样式  
        style2.setFont(font2); 
        return style2;
	}
	
	/**
	 * 创建列头
	* @param list
	* @param sheet
	* @param wb
	* @return void    
	* @Author:   zh
	* @Create Date:   2018年2月2日
	 */
	private void createHeader(List<EffectiveField> list,Sheet sheet,Workbook wb){
		//设置默认宽度为15
		sheet.setDefaultColumnWidth((short)15);
		 // 生成一个样式  
        CellStyle style = this.createHeadStyle(wb);
        // 生成一个字体  
        Font font = this.createHeadFont(wb);
        // 把字体应用到当前的样式  
        style.setFont(font);
        Row row = sheet.createRow(0);
        row.setHeightInPoints((short)15);
        Cell cellH = null;
        for(int i =0; i < list.size(); i++){
        	cellH = row.createCell(i);
        	cellH.setCellStyle(style);
        	cellH.setCellValue(list.get(i).getName());
        	sheet.setColumnWidth(i, list.get(i).getWidth() * 256);
        }
	}
	
	/**
	 * 根据传入信息生成表头
	* @param headers
	* @param sheet
	* @param wb
	* @return void    
	* @Author:   zh
	* @Create Date:   2018年2月7日
	 */
	private void createHead(String[] headers,Sheet sheet,Workbook wb) {
		// 设置表格默认列宽度为15个字节  
        sheet.setDefaultColumnWidth((short) 15);  
        // 生成一个样式  
        CellStyle style = this.createHeadStyle(wb);
        // 生成一个字体  
        Font font = this.createHeadFont(wb);
        // 把字体应用到当前的样式  
        style.setFont(font);
        Row row = sheet.createRow(0);
        row.setHeightInPoints((short)15);
        Cell cellH = null;
        for (short i = 0; i < headers.length; i++){  
        	cellH = row.createCell(i);  
        	cellH.setCellStyle(style);  
            cellH.setCellValue(headers[i]);  
            sheet.setColumnWidth(i, 20*256);
        }  
	}
	
	/**
	 * 列头样式
	* @param wb
	* @return
	* @return CellStyle    
	* @Author:   zh
	* @Create Date:   2018年2月2日
	 */
	private CellStyle createHeadStyle(Workbook wb) {
		 // 生成一个样式  
       CellStyle style = wb.createCellStyle();  
       // 设置这些样式  
       style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
       style.setFillForegroundColor(HSSFColor.TEAL.index);
       style.setBorderBottom(HSSFCellStyle.BORDER_THIN);  
       style.setBorderLeft(HSSFCellStyle.BORDER_THIN);  
       style.setBorderRight(HSSFCellStyle.BORDER_THIN);  
       style.setBorderTop(HSSFCellStyle.BORDER_THIN);  
       style.setAlignment(HSSFCellStyle.ALIGN_CENTER);  
       return style;
	}
	
	/**
	 * 列头字体
	* @param wb
	* @return
	* @return Font    
	* @Author:   zh
	* @Create Date:   2018年2月2日
	 */
	private Font createHeadFont(Workbook wb) {
		Font font = wb.createFont();  
       font.setColor(HSSFColor.WHITE.index);  
       font.setFontHeightInPoints((short) 11);  
       font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
       font.setFontName("宋体");
       return font;
	}
	/**
	 * 解析实体中需要生成列的字段
	* @param t
	* @return
	* @return List<EffectiveField>    
	* @Author:   zh
	* @Create Date:   2018年2月2日
	 */
	private List<EffectiveField> analysisFields(T t){
		List<EffectiveField> list = new ArrayList<>();
		Field[] fields = t.getClass().getDeclaredFields();
		for(Field f : fields){
			ExcelColumn eColumn = f.getAnnotation(ExcelColumn.class);
			if (eColumn != null) {
				EffectiveField acField = new EffectiveField();
				acField.setName(eColumn.name());
				acField.setWidth(eColumn.width());
				acField.setOrder(eColumn.order());
				acField.setField(f);
				acField.setFormat(eColumn.format());
				list.add(acField);
			}
		}
		//进行排序，order越小越靠前
		Collections.sort(list, new Comparator<EffectiveField>() {

			@Override
			public int compare(EffectiveField o1, EffectiveField o2) {
				// TODO Auto-generated method stub
				return o1.getOrder() - o2.getOrder();
			}
		});
		return list;
	}
}
